|
PowerPC G5 プロセッサは、アップルの最新コンピュータモデルの心臓部です。 前世代の G4 CPU からクロック周波数が明らかに高くなりましたが、コアの CPU とシステムアーキテクチャにいくつかの重要な変更がなされたため、これらのシステムにおけるプログラムの動作が影響を受けることがあります。 本稿では、プログラムを G5 上で動作させることを目指す上で、留意すべき主立った特徴について概説します。
[2003 年 9 月 11 日]
|
機能比較:G4 と G5
表 1 〜 3 は、G4 と G5 プロセッサの各種特徴を比較するための早見表です。 これらの相違点の詳細については、後述する各セクションの冒頭で概説します。
表 1. コアの特徴の比較
|
G4 |
G5 |
ビット |
32 |
64 |
クロックスピード (GHz) |
0.55 - 1.42 |
1.6 - 2.0 |
命令/クロック |
3 + 1 分岐 |
4 + 1 分岐 |
ロード/ストアユニット |
1 |
2 |
整数演算ユニット |
3 単純+ 1 複雑 |
2 |
浮動小数点演算ユニット |
1 |
2 |
ベクタ演算ユニット |
1 |
1 |
|
表 2. キャッシュの比較
|
G4 |
G5 |
キャッシュライン幅(バイト)
|
32 |
128 |
一次命令キャッシュ
|
32K、8 ウェイ連想 |
64K、ダイレクトマップ |
一次データキャッシュ
|
32K、ライトバック、8 ウェイ連想 |
32K、ライトスルー、2 ウェイ連想 |
二次キャッシュ (KB)
|
256 |
512 |
三次キャッシュ (MB)
|
2 |
0 |
|
表 3. メモリサブシステムの比較
|
G4 |
G5 |
データバス幅(ビット)
|
64 |
128 |
アドレスバス幅(ビット)
|
36 |
42 |
バススピード (MHz)
|
167 |
800-1000 |
バスバンド幅(GB/sec)
|
1.3 |
8.0 |
メモリバンド幅 (GB/sec)
|
2.7 |
6.4 |
レイテンシ (nSec)
|
93 |
135 |
アドレス可能なメモリ (GB)
|
2 |
16 |
|
先頭に戻る
G4 と G5 の違いのまとめ
G4 と比較すると、G5 は以下の点で異なります。
- コア:
- G5 は大規模な順不同実行エンジンを搭載しており、200 以上のインフライトの命令を維持できる(G4 は 30 命令)。
- 2 基の倍精度浮動小数点演算ユニット(G4 は 1 基)。
- 2 基のロード/ストアユニット(G4 は 1 基)。
- 64 ビット整数演算のサポート(G4 は 32 ビット)。
- フル精度の平方根をハードウェア命令として実装(G4 はソフトウェア関数)。
- ディスパッチから完了まで、命令を「グループ」単位で追跡。
- 複雑な命令を「分割」し、マイクロコードとして実装。
- 新しい形式の
mtcrf および mfcr ―― 古い命令はマイクロコードとして実装。
- Velocity Engine の単一要素ロードを
lvx として実装(以前のプロセッサでは未定義のフィールドをゼロに設定)。
- Velocity Engine の要素シフトでは、
VR の要素ごとにシフトカウントを複製する必要がある(以前のプロセッサでは、一番右にある要素のシフトカウントをすべてのシフトに使用)。
- 最大 23 ステージという非常に長い実行パイプライン(G4 は 7 ステージ)。
- 2 基の整数演算ユニット(G4 は 3 単純 + 1 複雑の 4 基)。 G5 の 2 基の整数演算ユニットは G4 の単純整数演算ユニットより高性能で、両方で乗算が可能なほか、一方で除算が可能(G4 の複雑整数演算ユニットは、乗算または除算のいずれか一方しか実行できない)。
- パイプラインが深いために、分岐予測ミスの場合は犠牲が大きくなる。
- キャッシュされていないメモリに対するロード/ストアのアラインメントのずれは、必ずアラインメント例外になる。
- キャッシュ:
- 512KB の二次キャッシュを搭載(G4 は 256KB)、三次キャッシュは非搭載。
- 128 バイトのキャッシュライン(G4 は 32 バイト)。
- 一次データキャッシュは 32K、ライトスルー、2 ウェイ連想(G4 は 32K、ライトバック、8 ウェイ連想)。
- ストアミス時には、一次キャッシュへの割り当てはない。
- 一次命令キャッシュは 64K、ダイレクトマッピング(G4 は 32K、8 ウェイ連想)。
- メモリサブシステム:
- システムメモリのバンド幅が大幅に拡張。
- 改善されたハードウェアプリフェッチ機構により、確立された順次アクセスパターンを自動スタート。
- U3 メモリコントローラによりアドレス指定可能なメモリ空間が最大 16GB に拡大(G4 は U2 メモリコントローラにより最大 2GB)。
- メモリレイテンシ(待ち時間)の増大、最高の条件で 135ns(G4 は最高の条件で 95ns)。
先頭に戻る
G5 のパフォーマンス向上のための推奨事項
追加された倍精度 FPU の利用
G5 は 2 基の完全な倍精度浮動小数点演算ユニットを搭載しており、それぞれが G4 に 1 基だけ搭載されている浮動小数点演算ユニット以上のパフォーマンスを発揮します。 2 基の FP スカラ演算ユニットは、ソフトウェアから見れば 2 ウェイの倍精度ベクタ演算ユニットととらえることができます。 追加の FPU を最大限活用するには、(ループ展開やソフトウェアパイプラインなどによって)依存関係を最小限にし、一方の FPU がコードのボトルネックにならないようにコードを作成します。 2 基の FPU で同時に実行できる FP コードを記述してください。 各ユニットの実行レイテンシ(待ち時間)が 6 サイクルであるため、ソフトウェアで 12 のパイプラインスロットを満たす必要があります。 ソフトウェア側では、CPU が 12 サイクルの実行レイテンシを伴う単一の FPU を搭載していると見なすと簡単になります。
ハードウェアプリフェッチエンジンの利用
G5 には、4 つの異なるストリームでデータを自動的にプリフェッチできる、自動スタートのプリフェッチエンジンが搭載されています。 プリフェッチは、ユーザの関与なしに開始されます。 順次キャッシュラインストライドで複数のロードミスのパターンが検出されると、プリフェッチエンジンは、確立されたパターンに従って、一次キャッシュと二次キャッシュに順次キャッシュラインをプリフェッチし始めます。 プリフェッチエンジンは要求ミスによって調整され、ページ境界 (4K) に到達するまで継続します。 同時に最大 4 つの確立されたプリフェッチストリームが独立にアクティブでいられます。 G5 がストアミスをプリフェッチしないことに注意してください。 プリフェッチは、DCBTL 命令を使用して開始することもできます。 DCBTL は DCBT とよく似た動作をしますが、特定の開始アドレスから複数のキャッシュラインがプリフェッチされ、プリフェッチストリームの方向を指定できます(上向きまたは下向き)。 プログラマがデータ使用パターンをあらかじめ知っている場合は、DCBTL によって、ハードウェアプリフェッチエンジンによって使用される自動ストリーム検出の始動コストを避けることができます。 DST と異なり、DCBTL で開始したプリフェッチはソフトウェアでは停止できませんが、ハードウェアプリフェッチエンジンと制約が同じです。
2 基のロード/ストアユニットの利用
G5 に搭載された追加のロード/ストアユニットにより、G4 より多くのメモリアクセスを 1 サイクルで処理できます。 このユニットとプロセッサから利用できる高いバンド幅の組み合わせにより、大量のデータを処理できる計算エンジンが実現しました。 2 基のロード/ストアユニットを利用できるようにコードを書き直せば(同時に、お互いが依存しないように注意する)、コードのパフォーマンスが大幅に向上します。
フル精度のハードウェア平方根機能の利用
G5 には、フル精度のハードウェア平方根機能が実装されています。 コードで平方根を算出する場合は、G5 のハードウェア平方根機能が利用できるかどうかを確認し、sqrt() ルーチンの代わりに、ハードウェアの命令を直接呼び出すコード(__fsqrt() など)を実行します (単精度の場合は、__fsqrts() を使用)。 GCC コンパイラのフラグ -mpowerpc-gpopt および -mpowerpc64 を使用すると、sqrt() 関数呼び出しを直接 PPC sqrt 命令に変換できます。
命令ディスパッチを最大化するホットコードの境界揃え
G5 のパフォーマンスにとって、メモリ内でのコードの境界揃えは G4 以上に重要な要素です。 G5 では、32 バイトの境界に揃えられたブロック単位で命令をフェッチします。 そのため、多くの場合は、ホットループ、分岐、または分岐ターゲットをフェッチ境界に揃えるほうが有利です。 GCC 3.3 には、コードを揃えるために、 -falign-functions=32 、-falign-labels=32 、-falign-loops=32 、-falign-jumps=32 といったフラグが用意されています。 さらに、境界に揃えるために、-falign-[functions, labels, loops, jumps]-max-skip=[15,31] の指定が必要な場合もあります。
重要:
このような境界揃えフラグを多用すると、GCC でコンパイルした実行コードの容量が大幅に増加します。
|
先頭に戻る
G5 のパフォーマンス低下を防ぐためにすべきこと
以前 G4 向けに最適化したコードについて、考慮すべき変更点の一覧を以下に示します。
DST とその派生命令の使用の評価を慎重に
Data Stream Touch 命令は、G5 では投機的に実行できないため、実行が直列化されます。 このような命令には、DST や DSTST などがあります。 DST 命令に遭遇した場合は、DST の実行前に完了しておけるように、実行エンジン全体をドレインできるようにしておく必要があります。 これは、(PPC の設計に従って)いずれにせよ完全な実行が保証されていないソフトウェアによるプリフェッチに対応するために、プログラムの実行時間に大きなバブルを生む可能性があります。 G5 における DST の実装は、4K のページ境界を越えるフェッチを行いません。 DST がページ境界に遭遇すると、終了させられます。 DST のトランジェントヒントは無視され、ストライドは 2 の累乗と見なされます。 そのため、DST は慎重に使う必要があります。 場合によっては有益なこともありますが、一般的に 1 ページ (4K) 内に収まり、連続したデータのプリフェッチでは、G5 に組み込まれたハードウェアプリフェッチエンジンがうまく機能します。 DCBT には、DST で生じる実行直列化のペナルティは生じません。 DCBT の問題は、単一の DST によるプリフェッチで済む分について、複数の DCBT を発行する必要があるかもしれない点です。 連続的なメモリブロックをプリフェッチする場合は、複数の DCBT の代わりに DCBTL を使用できます。
DCBZ のセマンティクス
デベロッパは、DCBZ が 32 バイト単位のデータを対象にすることを前提にしていました。 従来の Macintosh PowerPC システムのキャッシュラインがずっと 32 バイトのサイズだったからです。 しかし、G5 のキャッシュラインサイズは 128 バイトです。 G5 の DCBZ 命令は、32 バイト単位でも動作します。 しかし、その動作はきわめて非効率的なものです。 DCBZ に遭遇すると、ゼロにするように要求された 32 バイトを含む 128 バイトのキャッシュライン全体がメモリからフェッチされます。 これは一般的に、キャッシュラインからのキャストを引き起こすため、ダーティの場合はメモリに書き出す必要があります。 128 バイトのキャッシュラインをロードすると、要求した 32 バイトがゼロにされます。 そのため、G5 の DCBZ は、既存プログラムのパフォーマンスに 3 つの有害な影響を与えます。 32 バイト単位のスライドで処理を行う既存の DCBZ 命令を使用するループは、冗長で不必要な DCBZ を発行することになります。 最大 75% の DCBZ は、以前の DCBZ によってすでにフェッチされ、要求したデータがすでにキャッシュにある状態となります。 しかし、後続の DCBZ は、キャッシュラインの 32 バイトのチャンクをゼロにするのに必要です。 DCBZ 間のストライドが 128 バイトより大きい場合は、大量のメモリバンド幅が無駄になります。 CPU はメモリに対してキャッシュラインサイズ単位の要求のみを作成でき、メモリバンド幅のわずか 25% しか役に立たないからです。 DCBZ を使用する大部分のコードの目的は、メモリへのストアミスを回避することです。 ほとんどの場合、G5 の実装はむしろストアミスを引き起こします。 そのため、DCBZ の使用はよく注意して判断する必要があります。 できれば、DCBZ の代わりに DCBZL を使用してください。 DCBZL は G4 上の DCBZ と同様に機能しますが、G5 に含まれているネイティブなキャッシュライン(128 バイト全体)で動作します ((DCBZL は必ずネイティブなキャッシュラインサイズで動作します。G4 の場合は 32 バイト、G5 の場合は 128 バイトです)。 DCBZL を使用するには、CPU のキャッシュラインサイズを OS に照会し、キャッシュラインサイズを考慮したコードを記述する必要があります。
DCBI と DCBA は不正
G5 は DCBI または DCBA 命令をサポートしていません。 これらの命令は使用しないでください。
命令のレイテンシが延長
G4 の動作のレイテンシに依存した非常にタイトなループを記述した場合は、コードが G5 上でストールするおそれがあります。 このようなストールには、よりよいコードスケジュール、ループ展開、ソフトウェアパイプラインによって対処できます。 メモリに対するレイテンシも延びました。 CPU の周波数がメモリ周波数よりもずっと速いペースで上がったため、G4 の場合に比べると G5 の相対的なメモリアクセス時間も長くなりました。 可能なら、ループを展開して、データを使用する前にできるだけ早くアクセスする必要があります。 DCBT を使用するかストライドが規則的で、早期に確立できれば、ハードウェアプリフェッチエンジンを使用してプリフェッチを実行できます。 確立すべきもう 1 つのシナリオは不変のロード、つまりループ内で変化しないデータのロードです。 不変のロードをループ外に移動すると、プロセッサはループの反復のたびに変化しないデータをメモリから再フェッチする必要がなくなります。 これらの不要なメモリアクセスを取り除けば、パフォーマンスが大幅に向上します。 しばしば、グローバル変数の使用が不要なメモリアクセスを引き起こします。 コンパイラは最悪のエイリアシング状態を想定する必要があるため、グローバル変数の使用に際しては非常に保守的にならざるをえません。 そのため、値をレジスタに保持するよりも、コンパイラは正確さを確保するためにメモリからロードし、メモリにストアします。
Velocity Engine の命令発行に関する制約
G5 に搭載されている Velocity Engine ユニットに対する命令の発行は、7400/7410 G4 の場合と同じです。 命令の 1 つがベクタ変換の場合は、Velocity Engine ユニットに対して同一サイクルで発行できる命令は 2 つだけです。 745X G4 では、発行に関する制約が緩和され、各サイクルで任意の Velocity Engine 命令を 2 つ発行できるようになりました。 Velocity Engine に対する命令発行の制約に応じたコードを書いている場合、G5 では 7400 をターゲットとするコードを使用してください。 もちろん、G5 Velocity Engine パイプラインの増大したレイテンシに対処するために、さらにコードを再構築する必要があるかもしれません。 小さなデータのアクセスは避けてください。 メモリに対するレイテンシの増加、長くなったキャッシュライン、CPU−メモリ間バスの特性のために、小さなデータへのアクセスはなるべく避ける必要があります。 システムアーキテクチャ全体が、大量データの転送を最適化(つまり、システムメモリのスループットを最大化)するように設計されています。 その副作用として、小さなアクセスを処理するコストが非常に高く、非常に効率的でない場合があります。 できれば、データを大きなかたまりで割り当てるようにして、メモリアクセスのオーバーヘッドに対応してください。 キャッシュを小さくするように調整してください。 三次キャッシュの存在を前提にしてチューニングされていた高性能プログラムは、(増大した)512K の二次キャッシュに合わせて書き直す必要があります。 ERAT (Effective-to-Real Address Translation) キャッシュには 128 のエントリがあり、512K、つまり二次キャッシュと同じ容量のデータをマッピングするには十分です。 したがって、コードを 512K の二次キャッシュに合わせて最適化すると、処理において ERAT キャッシュを最大限に利用できます。
間違った分岐予測の回避
できるだけ直線的コード、インライン関数呼び出し、および展開ループを記述してください。 アセンブラコーダは、++ および -- サフィックスによって新しい AT 分岐予測ビットを使用して、例外チェックに使用するような非常に予測しやすい分岐を静的に予測できます。
ロックの使用の節減
実行パイプラインステージ数の増加とメモリに対するレイテンシの拡大により、ロックにアクセスして獲得する時間は、G4 よりも最大 2.5 倍遅くなることがあります。 ロックの実行をスピードアップするために実行できることはほとんどありませんが、コードで使用するロックの数を少なくすると、G5 の全般的なパフォーマンスが大幅に向上します。
lwarx 命令による予約の精度は 1 キャッシュライン分です。 これは G4 の 32 バイトから G5 の128 バイトに拡大したため、lwarx /stwcx のペアを使用する同期プリミティブ命令が G5 で失敗する可能性は 4 倍になっています。 具体的にいえば、同じキャッシュラインに複数の mutex をストアするのは避けてください。
コストのかかる型変換
スカラ型の変換は、メモリアクセスが必要ないため、PPC 上でのパフォーマンスは期待できません。 G5 では、特定のアドレスへのストアのあとにロードが続くとディスパッチグループの拒否やフラッシュが生じるため、問題が拡大します。 このようなバブルにより、高性能コードが大きな影響を受ける可能性があります。 パフォーマンスが重要なコードで浮動小数点から整数へ、またはその逆に変換する必要がある場合は、前のストアからロードを分離するために、コードに nop を埋め込むことを検討してください。 GCC では現在のところ、このような最適化を実行できませんが、将来 -mtune=power4 または -mcpu=power4 最適化フラグにこの機能を持たせていることが考えられます。
浮動小数点変数の整数部分を取得するには、float から int 、そこから float への代入の代わりに、floor() 関数を使用する必要があります。
マイクロコードの回避
G5 のコアには、いくつかの命令がマイクロコードで実装されています。 これらの命令は、デコード時にパイプラインバブルを引き起こします。 最もよく使用されるマイクロコード命令は、load multiple と store multiple(lmw と stmw )です。 これらは多くの場合、スタック上のレジスタを保存、復元する際にスペースを節約するために、コンパイラによって生成されます。 -mnomultiple を指定することで、GCC にこれらの命令を回避させることができます。 インデックス方式および代数方式のロードとストアの更新も、マイクロコードとして実行されます。 -mno-update を指定することで、GCC にこれらの命令を回避させることができます。
重要:
これらのコード生成フラグを多用すると、GCC でコンパイルした実行コードの容量が大幅に増加します。
|
先頭に戻る
G5 でコードを最適に動作させる方法
現在のコードが G5 で失敗する箇所を割り出すには、Shark(<http://developer.apple.com/tools/performance/> で入手可能な CHUD ツールパッケージに含まれています)を使用します。 Shark は(DCBZ や DST の有無など)バイナリを静的に分析して、潜在的にパフォーマンスの低いコードを見つけ出し、プログラム実行時に時間のかかるコードの部分を動的に測定できます。 さらに、Shark は、G5 コアにおけるディスパッチグループの形成と実行ユニットの使用をモデル化します。 これらは、G5 で良好なパフォーマンスを実現するための重要な要素です。 GCC 3.3 を使用して、最適化フラグを試してください。 GCC には、幅広いオプションが用意されています。 どのオプションが G5 で一番効果を発揮するかは、推測で判断するべきではありません。 必ずさまざまなオプションを試し、プロファイル結果に基づいてどのオプションがコードに最適かを判断してください。 利用可能な G5 最適化フラグの詳細については、TN2086 で解説しています。
G5 で動作するようにプログラムのコードをチューニングすると、その過程で G4 でも動作の速いコードになることがあります。 もちろん、G3/G4/G5 向けに最適化するために複数のコードパスが必要になる場合もありますが、パフォーマンスを優先する場合、選択肢は多くありません。 コードで最適な実行パスを選択できるように、Gestalt 、sysctl 、_cpu_capabilities を使用して、ターゲットプラットフォームで利用できる機能(Velocity Engine、DCBA 、DST など)を判断できます。
先頭に戻る
参考資料
Apple's Developer Connection Documentation
URL
先頭に戻る
|